#!/usr/bin/evn python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   文件名称 :     locsasl
   文件功能描述 :   功能描述
   创建人 :       小钟同学
   创建时间 :          2021/12/13
-------------------------------------------------
   修改描述-2021/12/13:
   PS：代码主要参考值flask框架
-------------------------------------------------
"""

import copy
from threading import get_ident
import typing
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


def release_local(local):
    '''
    清空相关的字典内的数据
    传入的是一个对象，对象里面调用的是它的一个叫做__release_local__（）的方法
    :param local:
    :return:
    '''
    local.__release_local__()


# 存储本地参数的堆栈：
# LocalStack与Local对象类似，区别在于其数据结构是栈的形式，而Local是字典的形式
# Local相当于dict，LocalStack相当于栈
class LocalStack:
    """This class infirmary_tasks similar to a :class:`Local` but keeps a stack
      of objects instead.  This is best explained with an example::
          >>> ls = LocalStack()
          >>> ls.push(42)
          >>> ls.top
          42
          >>> ls.push(23)
          >>> ls.top
          23
          >>> ls.pop()
          23
          >>> ls.top
          42
      They can be force released by using a :class:`LocalManager` or with
      the :func:`release_local` function but the correct way is to pop the
      item from the stack after using.  When the stack is empty it will
      no longer be bound to the current context (and as such released).
      By calling the stack without arguments it returns a proxy that resolves to
      the topmost item on the stack.
      .. versionadded:: 0.6.1

      """

    def __init__(self):
        self._local = Local()

    def __release_local__(self):
        self._local.__release_local__()

    @property
    def __ident_func(self):  # pylint: disable=unused-private-member
        return self._local.__ident_func__

    @__ident_func.setter
    def __ident_func__(self, value):
        object.__setattr__(self._local, "__ident_func__", value)

    def __call__(self):
        def _lookup():
            rv = self.top
            if rv is None:
                raise RuntimeError("Object unbond")
            return rv

        return LocalProxy(_lookup)

    def push(self, obj):
        """ 将一个数据入栈
        """
        # 判断字典线程字典里面是否存在stack的属性
        rv = getattr(self._local, "stack", None)
        if rv is None:
            self._local.stack = rv = []  # pylint: disable=assigning-non-slot
        rv.append(obj)
        return rv

    def pop(self):
        # 判断字典线程字典里面是否存在stack的属性
        stack = getattr(self._local, "stack", None)
        if stack is None:
            return None
        # 栈顶
        if len(stack) == 1:
            self._local.__release_local__()
            return stack[-1]

        return stack.pop()

    @property
    def top(self):
        """ 返回栈顶元素
        """
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None


class Local:
    """ Local 相当于一个字典，key为当前线程ID，
    对Local中的数据进行操作，相当于对线程内的数据进行操作，实现了线程之间数据隔离，
    对key操作其实就是对：当前线程ID内获取到对应的dict存储空间
    # Python内置的字典本质是一个哈希表，它是一种用空间换时间的数据结构。为了解决冲突的问题，
    #当字典使用量超过2/3时，Python会根据情况进行2-4倍的扩容。
    #由此可预见，取消__dict__的使用可以大幅减少实例的空间消耗
    # ===================
    ：问题点：
    --1：普通类身上时，使用__slots__后会丧失动态添加属性和弱引用的功能
    --2：当一个类需要创建大量实例时，可以使用__slots__来减少内存消耗
    --3：slots的特性来限制实例的属性
    """
    # 这个字典仅仅只能有这两个的属性存在
    __slots__ = ("__storage__", "__ident_func__")

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    # 当调用【Local对象】时，返回对应的LocalProxy
    def __call__(self, proxy):
        """ 创建某一个name的Proxy,
        返回一个代理的对象
        """
        return LocalProxy(self, proxy)

    def __release_local__(self):
        '''
        # Local类中特有的method，用于清空greenlet id或线程id对应的dict数据
        :return:
        '''
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        '''
        获取某线程内的某参数的执行信息
        :param name:
        :return:
        '''
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)  # pylint: disable=raise-missing-from

    def __setattr__(self, name, value):
        # 执行了__setattr__，创建了{ident:{stack:any}}
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)  # pylint: disable=raise-missing-from


class LocalProxy:
    """Acts as a proxy for a werkzeug local.  Forwards all operations to
       a proxied object.  The only operations not supported for forwarding
       are right handed operands and any kind of assignment.
       Example usage::
           from werkzeug.local import Local
           l = Local()
           # these are proxies
           request = l('request')
           user = l('user')
           from werkzeug.local import LocalStack
           _response_local = LocalStack()
           # this is a proxy
           response = _response_local()
       Whenever something is bound to l.user / l.request the proxy objects
       will forward all operations.  If no object is bound a :exc:`RuntimeError`
       will be raised.
       To create proxies to :class:`Local` or :class:`LocalStack` objects,
       call the object as shown above.  If you want to have a proxy to an
       object looked up by a function, you can (as of Werkzeug 0.6.1) pass
       a function to the :class:`LocalProxy` constructor::
           session = LocalProxy(lambda: get_current_request().session)
       .. versionchanged:: 0.6.1
          The class can be instantiated with a callable as well now.
       """
    """ LocalProxy 作为 Local 的代理，转发所有的操作到Local中的实际数据
    未封装前操作数据需要用 `local.xxx` 的语法进行操作，封装时候直接操作数据就行
    目的：
    使用代理而不是显式的对象的主要目的在于这四个对象使用太过频繁，贯穿整个请求周期，显式传递很容易造成循环导入的问题，需要一个第三方的对象来进行解耦
    """

    __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")  # pylint: disable=class-variable-slots-conflict

    def __init__(self, local, name=None):
        # 是这代理对象代理的local对象
        object.__setattr__(self, "_LocalProxy__local", local)
        # 设置代码对象的名
        object.__setattr__(self, "__name__", name)
        # 1：被代理的对象必须是一个callable【callable() 函数用于检查一个对象是否是可调用的】
        # 2:被代理的对象必须有__release_local__的属性，释放内部的字典数据
        if callable(local) and not hasattr(local, "__release_local__"):
            object.__setattr__(self, "__wrapped__", local)

    def _get_current_object(self)->typing.Any:
        """
        '''返回当前对象。如果出于性能原因您一次希望将真实对象放在代理后面，
    	或者因为要将对象传递到不同的上下文，这将很有用。
    	'''
        '''
        1.由于所有Local或LocalStack对象都有__release_local__ method, \
        	所以如果没有该属性就表明self.__local为callable对象。
       	2.当初始化参数为callable对象时，则直接调用以返回Local或LocalStack对象
       	'''
        """
        # 如果被代码的对象没有__release_local__ 那么就的返回__local()实例化的对象
        if not hasattr(self.__local, "__release_local__"):
            return self.__local()
        try:
            # 此处self.__local为Local或LocalStack对象
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError(f"no object bond to {self.__name__}")  # pylint: disable=(raise-missing-from

    @property
    def __dict__(self):
        '''
        _get_current_object当前对象的字典序列化返回
        :return:
        '''
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError("__dict__")  # pylint: disable=(raise-missing-from

    def __repr__(self):
        '''
         _get_current_object__repr__()方法：显示属性
        :return:
        '''
        try:
            obj = self._get_current_object()
        except RuntimeError:
            return "<%s unbond>" % self.__class__.__name__
        return repr(obj)

    def __bool__(self):
        try:
            return bool(self._get_current_object())
        except RuntimeError:
            return False

    def __dir__(self):
        '''
        返回当前对象的自省性格的属性信息
        :return:
        '''
        try:
            return dir(self._get_current_object())
        except RuntimeError:
            return []

    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

    def __setitem__(self, key, value):

        self._get_current_object()[key] = value

    def __delitem__(self, key):
        del self._get_current_object()[key]

    # 重载了绝大多数操作符，以便在调用LocalProxy的相应操作时，
    # 通过_get_current_object method来获取真正代理的对象，然后再进行相应操作
    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    __str__ = lambda x: str(x._get_current_object())
    __lt__ = lambda x, o: x._get_current_object() < o
    __le__ = lambda x, o: x._get_current_object() <= o
    __eq__ = lambda x, o: x._get_current_object() == o
    __ne__ = lambda x, o: x._get_current_object() != o
    __gt__ = lambda x, o: x._get_current_object() > o
    __ge__ = lambda x, o: x._get_current_object() >= o
    __hash__ = lambda x: hash(x._get_current_object())
    __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
    __len__ = lambda x: len(x._get_current_object())
    __getitem__ = lambda x, i: x._get_current_object()[i]
    __iter__ = lambda x: iter(x._get_current_object())
    __contains__ = lambda x, i: i in x._get_current_object()
    __add__ = lambda x, o: x._get_current_object() + o
    __sub__ = lambda x, o: x._get_current_object() - o
    __mul__ = lambda x, o: x._get_current_object() * o
    __floordiv__ = lambda x, o: x._get_current_object() // o
    __mod__ = lambda x, o: x._get_current_object() % o
    __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
    __pow__ = lambda x, o: x._get_current_object() ** o
    __lshift__ = lambda x, o: x._get_current_object() << o
    __rshift__ = lambda x, o: x._get_current_object() >> o
    __and__ = lambda x, o: x._get_current_object() & o
    __xor__ = lambda x, o: x._get_current_object() ^ o
    __or__ = lambda x, o: x._get_current_object() | o
    __div__ = lambda x, o: x._get_current_object().__div__(o)
    __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
    __neg__ = lambda x: -(x._get_current_object())
    __pos__ = lambda x: +(x._get_current_object())
    __abs__ = lambda x: abs(x._get_current_object())
    __invert__ = lambda x: ~(x._get_current_object())
    __complex__ = lambda x: complex(x._get_current_object())
    __int__ = lambda x: int(x._get_current_object())
    __float__ = lambda x: float(x._get_current_object())
    __oct__ = lambda x: oct(x._get_current_object())
    __hex__ = lambda x: hex(x._get_current_object())
    __index__ = lambda x: x._get_current_object().__index__()
    __coerce__ = lambda x, o: x._get_current_object().__coerce__(x, o)
    __enter__ = lambda x: x._get_current_object().__enter__()
    __exit__ = lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw)
    __radd__ = lambda x, o: o + x._get_current_object()
    __rsub__ = lambda x, o: o - x._get_current_object()
    __rmul__ = lambda x, o: o * x._get_current_object()
    __rdiv__ = lambda x, o: o / x._get_current_object()
    __rtruediv__ = __rdiv__
    __rfloordiv__ = lambda x, o: o // x._get_current_object()
    __rmod__ = lambda x, o: o % x._get_current_object()
    __rdivmod__ = lambda x, o: x._get_current_object().__rdivmod__(o)
    __copy__ = lambda x: copy.copy(x._get_current_object())
    __deepcopy__ = lambda x, memo: copy.deepcopy(x._get_current_object(), memo)


if __name__ == '__main__':
    # use Local object directly
    user_stack = LocalStack()
    # 在堆栈中入栈两个键值对，键都命名为name;
    user_stack.push({'name': 'Bob'})
    user_stack.push({'name': 'John'})
    def get_user():
        # 出栈这两个键值对（获取user对象）
        return user_stack.pop()
    # 直接调用函数获取user对象
    # 区别：直接使用LocalStack对象，user一旦赋值就无法再动态更新了，print(user['name'])
    # user = get_user()
    # 而使用Proxy，每次调用操作符(这里[]操作符用于获取属性)，都会重新获取user，从而实现了动态更新user的效果。
    user = LocalProxy(get_user)
    # 打印name的value值
    print(user['name'])
    print(user['name'])
